Coverage Report

Created: 2024-12-19 06:34

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
D:\a\tools.proto\tools.proto\compiler\src\compiler\protocol.rs
Line
Count
Source
1
// Copyright (c) 2024, BlockProject 3D
2
//
3
// All rights reserved.
4
//
5
// Redistribution and use in source and binary forms, with or without modification,
6
// are permitted provided that the following conditions are met:
7
//
8
//     * Redistributions of source code must retain the above copyright notice,
9
//       this list of conditions and the following disclaimer.
10
//     * Redistributions in binary form must reproduce the above copyright notice,
11
//       this list of conditions and the following disclaimer in the documentation
12
//       and/or other materials provided with the distribution.
13
//     * Neither the name of BlockProject 3D nor the names of its contributors
14
//       may be used to endorse or promote products derived from this software
15
//       without specific prior written permission.
16
//
17
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
18
// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
19
// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
20
// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
21
// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
22
// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
23
// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
24
// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
25
// LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
26
// NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
27
// SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
28
29
use crate::compiler::error::Error;
30
use crate::compiler::imports::Import;
31
use crate::compiler::message::{FieldType, Message};
32
use crate::compiler::r#enum::Enum;
33
use crate::compiler::structure::Structure;
34
use crate::compiler::union::Union;
35
use crate::compiler::util::imports::{ImportSolver, ProtocolStore};
36
use crate::compiler::util::store::{name_index, ObjectStore};
37
use crate::compiler::util::types::{Name, TypePathMap};
38
use crate::model::message::MessageFieldValue;
39
use crate::model::protocol::{Description, Endianness};
40
use crate::model::typedef::Typedef;
41
use bp3d_debug::{info, trace};
42
use std::borrow::Cow;
43
use std::collections::BTreeMap;
44
use std::rc::Rc;
45
46
name_index!(Typedef => name);
47
48
#[derive(Clone, Debug)]
49
pub struct Protocol {
50
    pub full_name: String,
51
    pub description: Option<Description>,
52
    pub endianness: Endianness,
53
    pub type_path_map: TypePathMap,
54
    pub structs: ObjectStore<Structure>,
55
    pub messages: ObjectStore<Message>,
56
    pub enums: ObjectStore<Enum>,
57
    pub unions: ObjectStore<Union>,
58
    pub types: ObjectStore<Typedef>,
59
}
60
61
impl bp3d_util::index_map::Index for Protocol {
62
    type Key = str;
63
64
171
    fn index(&self) -> &Self::Key {
65
171
        &self.full_name
66
171
    }
67
}
68
69
impl Protocol {
70
35
    pub fn name(&self) -> &str {
71
35
        if let Some(
id0
) = self.full_name.rfind("::") {
  Branch (71:16): [True: 0, False: 35]
  Branch (71:16): [Folded - Ignored]
72
0
            &self.full_name[id + 2..]
73
        } else {
74
35
            &self.full_name
75
        }
76
35
    }
77
78
38
    pub fn package(&self) -> &str {
79
38
        if let Some(
id0
) = self.full_name.rfind("::") {
  Branch (79:16): [True: 0, False: 38]
  Branch (79:16): [Folded - Ignored]
80
0
            &self.full_name[..id]
81
        } else {
82
38
            ""
83
        }
84
38
    }
85
86
70
    pub fn iter_codecs(&self) -> impl Iterator<Item = &str> {
87
98
        self.messages.iter().flat_map(|v| 
v.fields.iter().filter_map(56
|v| v.codec.as_deref()
)56
)
88
70
    }
89
90
55
    pub fn from_model<T: ImportSolver>(
91
55
        mut value: crate::model::Protocol,
92
55
        protocols: &ProtocolStore<T>,
93
55
        package: &str,
94
55
    ) -> Result<Self, Error> {
95
55
        let full_name = if package.is_empty() {
  Branch (95:28): [True: 17, False: 0]
  Branch (95:28): [Folded - Ignored]
  Branch (95:28): [True: 18, False: 0]
  Branch (95:28): [Folded - Ignored]
  Branch (95:28): [True: 5, False: 0]
  Branch (95:28): [True: 1, False: 0]
  Branch (95:28): [True: 1, False: 0]
  Branch (95:28): [True: 4, False: 0]
  Branch (95:28): [True: 3, False: 0]
  Branch (95:28): [True: 5, False: 0]
  Branch (95:28): [True: 1, False: 0]
96
55
            value.name
97
        } else {
98
0
            format!("{}::{}", package, value.name)
99
        };
100
55
        let mut proto = Protocol {
101
55
            full_name,
102
55
            description: value.description,
103
55
            endianness: value.endianness.unwrap_or(Endianness::Little),
104
55
            type_path_map: TypePathMap::new(),
105
55
            structs: ObjectStore::new(),
106
55
            messages: ObjectStore::new(),
107
55
            enums: ObjectStore::new(),
108
55
            unions: ObjectStore::new(),
109
55
            types: ObjectStore::new(),
110
55
        };
111
55
        info!("Running import solver pass...");
112
55
        if let Some(
mut imports10
) = value.imports {
  Branch (112:16): [True: 5, False: 12]
  Branch (112:16): [Folded - Ignored]
  Branch (112:16): [True: 5, False: 13]
  Branch (112:16): [Folded - Ignored]
  Branch (112:16): [True: 0, False: 5]
  Branch (112:16): [True: 0, False: 1]
  Branch (112:16): [True: 0, False: 1]
  Branch (112:16): [True: 0, False: 4]
  Branch (112:16): [True: 0, False: 3]
  Branch (112:16): [True: 0, False: 5]
  Branch (112:16): [True: 0, False: 1]
113
10
            let mut solved_imports = Vec::new();
114
48
            while let Some(
v38
) = imports.pop() {
  Branch (114:23): [True: 19, False: 5]
  Branch (114:23): [Folded - Ignored]
  Branch (114:23): [True: 19, False: 5]
  Branch (114:23): [Folded - Ignored]
  Branch (114:23): [True: 0, False: 0]
  Branch (114:23): [True: 0, False: 0]
  Branch (114:23): [True: 0, False: 0]
  Branch (114:23): [True: 0, False: 0]
  Branch (114:23): [True: 0, False: 0]
  Branch (114:23): [True: 0, False: 0]
  Branch (114:23): [True: 0, False: 0]
115
38
                let protocol_path = if package.is_empty() {
  Branch (115:40): [True: 19, False: 0]
  Branch (115:40): [Folded - Ignored]
  Branch (115:40): [True: 19, False: 0]
  Branch (115:40): [Folded - Ignored]
  Branch (115:40): [True: 0, False: 0]
  Branch (115:40): [True: 0, False: 0]
  Branch (115:40): [True: 0, False: 0]
  Branch (115:40): [True: 0, False: 0]
  Branch (115:40): [True: 0, False: 0]
  Branch (115:40): [True: 0, False: 0]
  Branch (115:40): [True: 0, False: 0]
116
38
                    Cow::Borrowed(&v.protocol)
117
                } else {
118
0
                    Cow::Owned(format!("{}::{}", package, v.protocol))
119
                };
120
38
                trace!({protocol=&**protocol_path} {type=&*v.type_name}, "Solving import");
121
38
                let r = protocols.get(&protocol_path);
122
38
                let r = match r {
123
38
                    Some(r) => r,
124
0
                    None => return Err(Error::UndefinedReference(v.protocol)),
125
                };
126
38
                let ty = r
127
38
                    .structs
128
38
                    .get(&v.type_name)
129
38
                    .map(Import::Struct)
130
38
                    .or_else(|| 
r.messages.get(&v.type_name).map(Import::Message)12
)
131
38
                    .or_else(|| 
r.unions.get(&v.type_name).map(Import::Union)6
)
132
38
                    .or_else(|| 
r.enums.get(&v.type_name).map(Import::Enum)4
)
133
38
                    .or_else(|| 
r.types.get(&v.type_name).map(Import::Type)2
)
134
38
                    .ok_or(Error::UnresolvedImport(format!("{}::{}", protocol_path, v.type_name)))
?0
;
135
38
                let type_path = protocols.get_full_type_path(r, &v.type_name).ok_or(Error::SolverError)
?0
;
136
38
                solved_imports.push(ty);
137
160
                let count1 = imports.iter().filter(|vv| vv.type_name == v.type_name).count();
138
198
                let count2 = solved_imports.iter().filter(|vv| vv.name() == v.type_name
).count()38
;
139
38
                let is_ambiguous = count1 > 0 || 
count2 > 136
;
  Branch (139:36): [True: 1, False: 18]
  Branch (139:36): [Folded - Ignored]
  Branch (139:36): [True: 1, False: 18]
  Branch (139:36): [Folded - Ignored]
  Branch (139:36): [True: 0, False: 0]
  Branch (139:36): [True: 0, False: 0]
  Branch (139:36): [True: 0, False: 0]
  Branch (139:36): [True: 0, False: 0]
  Branch (139:36): [True: 0, False: 0]
  Branch (139:36): [True: 0, False: 0]
  Branch (139:36): [True: 0, False: 0]
140
38
                proto.type_path_map.add(&ty, type_path);
141
38
                if is_ambiguous {
  Branch (141:20): [True: 2, False: 17]
  Branch (141:20): [Folded - Ignored]
  Branch (141:20): [True: 2, False: 17]
  Branch (141:20): [Folded - Ignored]
  Branch (141:20): [True: 0, False: 0]
  Branch (141:20): [True: 0, False: 0]
  Branch (141:20): [True: 0, False: 0]
  Branch (141:20): [True: 0, False: 0]
  Branch (141:20): [True: 0, False: 0]
  Branch (141:20): [True: 0, False: 0]
  Branch (141:20): [True: 0, False: 0]
142
4
                    trace!({protocol=&**protocol_path} {type=&*v.type_name}, "Import is ambiguous, import it as {}::{}", v.protocol, v.type_name);
143
4
                    ty.insert(format!("{}::{}", v.protocol, v.type_name), &mut proto);
144
34
                } else {
145
34
                    trace!({protocol=&**protocol_path} {type=&*v.type_name}, "Import is not ambiguous");
146
34
                    ty.insert(v.type_name, &mut proto);
147
34
                }
148
            }
149
45
        }
150
55
        info!("Adding typedefs...");
151
55
        if let Some(
types3
) = value.types {
  Branch (151:16): [True: 1, False: 16]
  Branch (151:16): [Folded - Ignored]
  Branch (151:16): [True: 2, False: 16]
  Branch (151:16): [Folded - Ignored]
  Branch (151:16): [True: 0, False: 5]
  Branch (151:16): [True: 0, False: 1]
  Branch (151:16): [True: 0, False: 1]
  Branch (151:16): [True: 0, False: 4]
  Branch (151:16): [True: 0, False: 3]
  Branch (151:16): [True: 0, False: 5]
  Branch (151:16): [True: 0, False: 1]
152
11
            for 
v8
in types {
153
8
                trace!({model=?&v}, "Adding typedef to protocol");
154
8
                proto.types.insert(Rc::new(v));
155
8
            }
156
52
        }
157
55
        info!("Running type inference pass...");
158
55
        if let Some(
structs45
) = &mut value.structs {
  Branch (158:16): [True: 15, False: 2]
  Branch (158:16): [Folded - Ignored]
  Branch (158:16): [True: 16, False: 2]
  Branch (158:16): [Folded - Ignored]
  Branch (158:16): [True: 0, False: 5]
  Branch (158:16): [True: 1, False: 0]
  Branch (158:16): [True: 1, False: 0]
  Branch (158:16): [True: 4, False: 0]
  Branch (158:16): [True: 3, False: 0]
  Branch (158:16): [True: 5, False: 0]
  Branch (158:16): [True: 0, False: 1]
159
119
            for 
v74
in structs {
160
205
                for 
field131
in &mut v.fields {
161
0
                    if let Some(info) =
  Branch (161:28): [True: 0, False: 58]
  Branch (161:28): [Folded - Ignored]
  Branch (161:28): [True: 0, False: 59]
  Branch (161:28): [Folded - Ignored]
  Branch (161:28): [True: 0, False: 0]
  Branch (161:28): [True: 0, False: 1]
  Branch (161:28): [True: 0, False: 1]
  Branch (161:28): [True: 0, False: 4]
  Branch (161:28): [True: 0, False: 2]
  Branch (161:28): [True: 0, False: 6]
  Branch (161:28): [True: 0, False: 0]
162
131
                        field.item_type.as_ref().and_then(|v| 
proto.types.get(v)13
).and_then(|v|
v.to_struct()0
)
163
0
                    {
164
0
                        trace!({typedef=?info}, "Inferred {} as {}", field.name, info.name);
165
0
                        let name = std::mem::replace(field, info.clone()).name;
166
0
                        field.name = name;
167
131
                    }
168
                }
169
            }
170
10
        }
171
55
        if let Some(
messages25
) = &mut value.messages {
  Branch (171:16): [True: 7, False: 10]
  Branch (171:16): [Folded - Ignored]
  Branch (171:16): [True: 8, False: 10]
  Branch (171:16): [Folded - Ignored]
  Branch (171:16): [True: 5, False: 0]
  Branch (171:16): [True: 0, False: 1]
  Branch (171:16): [True: 0, False: 1]
  Branch (171:16): [True: 0, False: 4]
  Branch (171:16): [True: 0, False: 3]
  Branch (171:16): [True: 5, False: 0]
  Branch (171:16): [True: 0, False: 1]
172
64
            for 
v39
in messages {
173
103
                for 
field64
in &mut v.fields {
174
8
                    if let Some(info) =
  Branch (174:28): [True: 2, False: 20]
  Branch (174:28): [Folded - Ignored]
  Branch (174:28): [True: 6, False: 21]
  Branch (174:28): [Folded - Ignored]
  Branch (174:28): [True: 0, False: 6]
  Branch (174:28): [True: 0, False: 0]
  Branch (174:28): [True: 0, False: 0]
  Branch (174:28): [True: 0, False: 0]
  Branch (174:28): [True: 0, False: 0]
  Branch (174:28): [True: 0, False: 9]
  Branch (174:28): [True: 0, False: 0]
175
64
                        field.item_type.as_ref().and_then(|v| 
proto.types.get(v)24
).and_then(|v|
v.to_message()8
)
176
8
                    {
177
8
                        trace!({typedef=?info}, "Inferred {} as {}", field.name, info.name);
178
8
                        let name = std::mem::replace(field, info.clone()).name;
179
8
                        field.name = name;
180
56
                    }
181
                }
182
            }
183
30
        }
184
55
        info!("Running compiler pass...");
185
55
        if let Some(
enums7
) = value.enums {
  Branch (185:16): [True: 3, False: 14]
  Branch (185:16): [Folded - Ignored]
  Branch (185:16): [True: 3, False: 15]
  Branch (185:16): [Folded - Ignored]
  Branch (185:16): [True: 0, False: 5]
  Branch (185:16): [True: 0, False: 1]
  Branch (185:16): [True: 0, False: 1]
  Branch (185:16): [True: 0, False: 4]
  Branch (185:16): [True: 0, False: 3]
  Branch (185:16): [True: 0, False: 5]
  Branch (185:16): [True: 1, False: 0]
186
7
            trace!(">> Compiling enums...");
187
13
            for 
v7
in enums {
188
7
                trace!({model=?&v}, "Compiling enum");
189
7
                let 
v6
= Rc::new(Enum::from_model(v)
?1
);
190
6
                proto.enums.insert(v);
191
            }
192
48
        }
193
54
        if let Some(
structs45
) = value.structs {
  Branch (193:16): [True: 15, False: 2]
  Branch (193:16): [Folded - Ignored]
  Branch (193:16): [True: 16, False: 2]
  Branch (193:16): [Folded - Ignored]
  Branch (193:16): [True: 0, False: 5]
  Branch (193:16): [True: 1, False: 0]
  Branch (193:16): [True: 1, False: 0]
  Branch (193:16): [True: 4, False: 0]
  Branch (193:16): [True: 3, False: 0]
  Branch (193:16): [True: 5, False: 0]
  Branch (193:16): [True: 0, False: 0]
194
45
            trace!(">> Compiling structures...");
195
110
            for 
v74
in structs {
196
74
                trace!({model=?&v}, "Compiling structure");
197
74
                let 
v65
= Rc::new(Structure::from_model(&proto, v)
?9
);
198
65
                proto.structs.insert(v);
199
            }
200
9
        }
201
45
        let mut union_messages = BTreeMap::new();
202
45
        if let Some(
mut messages25
) = value.messages {
  Branch (202:16): [True: 7, False: 10]
  Branch (202:16): [Folded - Ignored]
  Branch (202:16): [True: 8, False: 10]
  Branch (202:16): [Folded - Ignored]
  Branch (202:16): [True: 5, False: 0]
  Branch (202:16): [True: 0, False: 0]
  Branch (202:16): [True: 0, False: 0]
  Branch (202:16): [True: 0, False: 0]
  Branch (202:16): [True: 0, False: 0]
  Branch (202:16): [True: 5, False: 0]
  Branch (202:16): [True: 0, False: 0]
203
25
            trace!(">> Extracting unions and messages with references to unions...");
204
25
            let len = messages.len();
205
39
            for i in 1..
len + 125
{
206
39
                let i = len - i;
207
64
                let has_unions = messages[i].fields.iter().any(|v| match &v.value {
208
16
                    None => false,
209
48
                    Some(v) => 
matches!39
(v, MessageFieldValue::Union { .. }),
210
64
                });
211
39
                if has_unions {
  Branch (211:20): [True: 2, False: 11]
  Branch (211:20): [Folded - Ignored]
  Branch (211:20): [True: 2, False: 13]
  Branch (211:20): [Folded - Ignored]
  Branch (211:20): [True: 0, False: 6]
  Branch (211:20): [True: 0, False: 0]
  Branch (211:20): [True: 0, False: 0]
  Branch (211:20): [True: 0, False: 0]
  Branch (211:20): [True: 0, False: 0]
  Branch (211:20): [True: 5, False: 0]
  Branch (211:20): [True: 0, False: 0]
212
9
                    let msg = messages.remove(i);
213
9
                    union_messages.insert(i, msg);
214
30
                }
215
            }
216
25
            let pos = len;
217
25
            let len = messages.len();
218
30
            for i in 1..
len + 125
{
219
30
                let i = len - i;
220
30
                let has_ref = union_messages.values().any(|union| 
messages[i].references(&union.name)6
);
221
30
                if has_ref {
  Branch (221:20): [True: 1, False: 10]
  Branch (221:20): [Folded - Ignored]
  Branch (221:20): [True: 1, False: 12]
  Branch (221:20): [Folded - Ignored]
  Branch (221:20): [True: 0, False: 6]
  Branch (221:20): [True: 0, False: 0]
  Branch (221:20): [True: 0, False: 0]
  Branch (221:20): [True: 0, False: 0]
  Branch (221:20): [True: 0, False: 0]
  Branch (221:20): [True: 0, False: 0]
  Branch (221:20): [True: 0, False: 0]
222
2
                    let msg = messages.remove(i);
223
2
                    union_messages.insert(pos + i, msg);
224
28
                }
225
            }
226
25
            trace!(">> Compiling messages with no references to unions...");
227
48
            for 
v28
in messages {
228
28
                trace!({model=?&v}, "Compiling message");
229
28
                let 
v23
= Rc::new(Message::from_model(&proto, v)
?5
);
230
23
                proto.messages.insert(v);
231
            }
232
20
        }
233
40
        if let Some(
unions10
) = value.unions {
  Branch (233:16): [True: 3, False: 14]
  Branch (233:16): [Folded - Ignored]
  Branch (233:16): [True: 3, False: 15]
  Branch (233:16): [Folded - Ignored]
  Branch (233:16): [True: 0, False: 0]
  Branch (233:16): [True: 0, False: 0]
  Branch (233:16): [True: 0, False: 0]
  Branch (233:16): [True: 0, False: 0]
  Branch (233:16): [True: 0, False: 0]
  Branch (233:16): [True: 4, False: 1]
  Branch (233:16): [True: 0, False: 0]
234
10
            trace!(">> Compiling unions...");
235
20
            for 
v10
in unions {
236
10
                trace!({model=?&v}, "Compiling union");
237
10
                let v = Rc::new(Union::from_model(&proto, v)
?0
);
238
10
                proto.unions.insert(v);
239
            }
240
30
        }
241
40
        trace!(">> Compiling messages with references to unions...");
242
46
        for (_, 
msg11
) in union_messages {
243
11
            trace!({model=?&msg}, "Compiling message");
244
11
            let 
v6
= Rc::new(Message::from_model(&proto, msg)
?5
);
245
6
            proto.messages.insert(v);
246
        }
247
248
35
        info!("Running list sanitizer pass...");
249
35
        for 
msg28
in proto.messages.iter() {
250
28
            if msg.is_embedded() {
  Branch (250:16): [True: 3, False: 10]
  Branch (250:16): [Folded - Ignored]
  Branch (250:16): [True: 4, False: 11]
  Branch (250:16): [Folded - Ignored]
  Branch (250:16): [True: 0, False: 0]
  Branch (250:16): [True: 0, False: 0]
  Branch (250:16): [True: 0, False: 0]
  Branch (250:16): [True: 0, False: 0]
  Branch (250:16): [True: 0, False: 0]
  Branch (250:16): [True: 0, False: 0]
  Branch (250:16): [True: 0, False: 0]
251
14
                for field in &
msg.fields7
{
252
14
                    let flag = match &field.ty {
253
2
                        FieldType::Container(v) => v.nested,
254
12
                        _ => true,
255
                    };
256
14
                    if !flag {
  Branch (256:24): [True: 0, False: 6]
  Branch (256:24): [Folded - Ignored]
  Branch (256:24): [True: 0, False: 8]
  Branch (256:24): [Folded - Ignored]
  Branch (256:24): [True: 0, False: 0]
  Branch (256:24): [True: 0, False: 0]
  Branch (256:24): [True: 0, False: 0]
  Branch (256:24): [True: 0, False: 0]
  Branch (256:24): [True: 0, False: 0]
  Branch (256:24): [True: 0, False: 0]
  Branch (256:24): [True: 0, False: 0]
257
0
                        return Err(Error::MissingNestedList(format!("{}::{}", msg.name, field.name)));
258
14
                    }
259
                }
260
21
            }
261
        }
262
35
        Ok(proto)
263
55
    }
264
}